#pragma once 
//util.hpp是一个工具集,所有的工具内容都写在这个里面

#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <unordered_set>
#include <mutex>

#include <boost/algorithm/string.hpp>

#include "./jiebacpp/Jieba.hpp"
#include "./log.hpp"

namespace ns_util
{
    class file_util
    {
    public:
        static bool read_file(const std::string& file_path, std::string* out)
        {
            std::ifstream in(file_path, std::ifstream::in);

            if(!in.is_open())
            {
                std::cerr << "open file " << file_path << "error" << std::endl;
                return false;
            }
            
            std::string line;
            //这里getline的返回值是一个输入流对象的引用
            //为什么可以作为while循环的判断条件呢?
            //这里输入流对象可以被视为一个bool值
            //这是这是因为输入流对象可以转换为bool类型，这种类型转换被称为“隐式转换”。
            //当输入流对象需要被转换为bool类型时
            //编译器会自动调用输入流对象的operator bool()函数
            //该函数返回输入流对象的状态
            //如果输入流对象没有到达文件结尾且没有出现读取错误
            //则返回true，否则返回false
            //这种将输入流对象转换为bool类型的机制，使得在代码中可以方便地使用输入流对象作为条件表达式，以判断输入流对象是否还有未读取的数据
            while(std::getline(in, line))
            {
                *out += line;
            }

            in.close();
            return true;
        }
    };

    class string_util
    {
    public:
        static void split_string(const std::string& target, std::vector<std::string>* out, const std::string& separator)
        {
            boost::split(*out, target, boost::is_any_of(separator), boost::token_compress_on);
        }
    };

    const char* const DICT_PATH = "./dict/jieba.dict.utf8";
    const char* const HMM_PATH = "./dict/hmm_model.utf8";
    const char* const USER_DICT_PATH = "./dict/user.dict.utf8";
    const char* const IDF_PATH = "./dict/idf.utf8";
    const char* const STOP_WORD_PATH = "./dict/stop_words.utf8";

    class jieba_util
    {
    public:
        static void cut_string(const std::string& target, std::vector<std::string>* out)
        {
            //第一个参数是需要被分词的string
            //第二个参数是分词分好后的结果
            ns_util::jieba_util::get_instance()->cut_string_helper(target, out);
        }

        static jieba_util* get_instance()
        {
            if (instance == nullptr)
            {
                mtx.lock();
                if (instance == nullptr)
                {
                    instance = new jieba_util();
                    instance->init_jieba_util();
                }
                mtx.unlock();
            }
            return instance;
        }

    private:
        void cut_string_helper(const std::string& target, std::vector<std::string>* out)
        {
            jieba.CutForSearch(target, *out);

            for (auto iter = out->begin(); iter != out->end();)
            {
                auto it = stop_words.find(*iter);
                if (it != stop_words.end())
                {
                    //说明是暂停词,需要去掉
                    iter = out->erase(iter);
                }
                else 
                {
                    ++iter;
                }
            }
        }

        void init_jieba_util()
        {
            std::ifstream in(STOP_WORD_PATH);
            if (!in.is_open())
            {
                LOG(FATAL, "open stop words file error!");
                return;
            }

            std::string line;
            while(std::getline(in, line))
            {
                stop_words.insert(line);
            }
            in.close();
        }

        jieba_util() : jieba
            (DICT_PATH, 
            HMM_PATH, 
            USER_DICT_PATH, 
            IDF_PATH, 
            STOP_WORD_PATH)
            , stop_words()
            {}

    private:
        cppjieba::Jieba jieba;
        std::unordered_set<std::string> stop_words;
        static std::mutex mtx;
        static jieba_util* instance;
    };

    jieba_util* jieba_util::instance = nullptr;
    std::mutex jieba_util::mtx;
}

